package com.ironman.analyze.core.compute;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.ironman.analyze.core.compute.base.BaseComputer;
import com.ironman.analyze.core.compute.entity.WindEnvWindowData;
import com.ironman.analyze.core.context.TrainingContext;
import com.ironman.analyze.core.handler.WindPostHandler;
import com.ironman.common.entity.WindData;
import com.ironman.common.params.WindParam;
import com.ironman.common.structure.FixedSizeLinkedBlockingDeque;
import com.ironman.common.structure.WindVectorData;
import com.ironman.common.structure.WindWindowData;
import com.ironman.common.util.WindowCalUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 风矢量计算
 */
@Slf4j
public class WindVectorComputer implements BaseComputer<WindData, WindEnvWindowData> {

    private final Integer windowSize;

    private final FixedSizeLinkedBlockingDeque<WindVectorData> windVectorDataFixedSizeLinkedBlockingDeque;

    private final WindPostHandler windPostHandler;

    /**
     * 共享变量
     */
    private final TrainingContext trainingContext;

    public WindVectorComputer(TrainingContext trainingContext, WindParam windParam) {
        this.windowSize = windParam.getWindowSize();
        this.windVectorDataFixedSizeLinkedBlockingDeque = new FixedSizeLinkedBlockingDeque<>(windowSize);
        this.windPostHandler = new WindPostHandler(windParam);
        this.trainingContext = trainingContext;
    }

    @Override
    public void computeWindowData(List<WindData> dataList) {
        for (WindData windData : dataList) {
            this.windVectorDataFixedSizeLinkedBlockingDeque.offerLast(WindowCalUtil.preCompute(windData.getTrainingBoatCode(), windData.getTs(), windData.getDirection(), windData.getSpeed()));
            List<WindVectorData> windVectorDataList = this.windVectorDataFixedSizeLinkedBlockingDeque.subList(windowSize, true);
            if (CollectionUtil.isEmpty(windVectorDataList)) {
                log.debug("未能达到窗口大小，目标窗口大小为{}", windowSize);
                continue;
            }

            // 移除不在窗口数据
            Date maxTs = windVectorDataList.get(windowSize - 1).getArrivalDate();
            windVectorDataList = windVectorDataList.stream().filter(n -> DateUtil.between(n.getArrivalDate(), maxTs, DateUnit.SECOND) < windowSize).collect(Collectors.toList());
            log.debug("风开启窗口计算 目标窗口大小 {} 实际数据大小 {}", windowSize, windVectorDataList.size());

            WindWindowData windWindowData = WindowCalUtil.computeWindowData(windVectorDataList);
            WindEnvWindowData windEnvWindowData = new WindEnvWindowData(windWindowData.getArrivalDate(), windWindowData.getTrainingBoatCode(), windWindowData.getSpeed(), windWindowData.getDirection());
            windPostHandler.handler(windEnvWindowData, new Consumer<WindEnvWindowData>() {
                @Override
                public void accept(WindEnvWindowData windEnvWindowData) {
                    syncData(windEnvWindowData);
                }
            });
        }
    }

    @Override
    public void syncData(WindEnvWindowData windowData) {
        Date ts = windowData.getTs();
        String trainingBoatCode = windowData.getTrainingBoatCode();

        Map<String, Map<Date, WindEnvWindowData>> windAllWindowDataMapMap = trainingContext.getWindAllWindowDataMapMap();
        Map<Date, WindEnvWindowData> windEnvWindowDataMap = windAllWindowDataMapMap.get(trainingBoatCode);
        if (CollectionUtil.isEmpty(windEnvWindowDataMap)) {
            windEnvWindowDataMap = new HashMap<>();
            windAllWindowDataMapMap.put(trainingBoatCode, windEnvWindowDataMap);
        }
        windEnvWindowDataMap.put(ts, windowData);
    }
}
